package com.jgp.sys.repository;

import com.jgp.common.persistence.Model;
import com.jgp.common.persistence.TreeModel;
import com.jgp.common.persistence.TreeModelStructure;
import com.jgp.common.persistence.UUIDModel;
import com.jgp.common.pojo.TreeBean;
import com.jgp.common.utils.JGPUtil;
import com.jgp.common.utils.ReflectUtil;
import com.jgp.sys.query.Operator;
import com.jgp.sys.query.OrderList;
import com.jgp.sys.query.QueryFilterList;
import com.jgp.sys.query.jpa.CriteriaExpression;
import com.jgp.sys.query.jpa.DataFilterBuilder;
import com.jgp.sys.query.power.DataPowerConfig;
import com.jgp.sys.ui.Pager;
import com.jgp.sys.utils.TreeUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.Tuple;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import javax.persistence.metamodel.EntityType;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 项目   jgp
 * 作者   loufei
 * 时间   2017/8/4
 */
@NoRepositoryBean
public class JGPRepositoryImpl<M extends Model, ID extends Serializable> extends SimpleJpaRepository<M, ID> implements JGPRepository<M, ID>,
        JpaRepository<M, ID> {
    
    private static DataFilterBuilder filterBuilder;
    
    static {
        filterBuilder = new DataFilterBuilder();
    }
    
    private final EntityManager entityManager;
    private final Class<M> javaType;
    private final String[] PLATFORM_COLUMNS = {"rowId", "delFlag", "createDatetime", "createTimestamp", "modifyDatetime", "modifyTimestamp"};
    private final Class idType;
    private final String entityName;
    private final Set<String> entityTypeNames;
    
    public JGPRepositoryImpl(JpaEntityInformation<M, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityName = entityInformation.getEntityName();
        this.javaType = entityInformation.getJavaType();
        this.idType = entityInformation.getIdType();
        this.entityManager = entityManager;
        Set<EntityType<?>> entityTypes = entityManager.getMetamodel().getEntities();
        if(CollectionUtils.isNotEmpty(entityTypes)){
            this.entityTypeNames = entityTypes.stream().map(EntityType::getName).collect(Collectors.toSet());
        }else {
            this.entityTypeNames = null;
        }
    }
    
    private static <M extends Model> M init(M m) {
        Class<?> mType = m.getClass();
        if (UUIDModel.class.isAssignableFrom(mType) && Objects.isNull(m.getId())) {
            m.setId(JGPUtil.uuid());
        }
        Long timestamp = nanoTime();
        LocalDateTime now = LocalDateTime.now();
        m.setDelFlag(false);
        m.setCreateDatetime(now);
        m.setModifyDatetime(now);
        m.setCreateTimestamp(timestamp);
        m.setModifyTimestamp(timestamp);
        return m;
    }
    
    private static <M extends Model> M refresh(M m) {
        Long timestamp = nanoTime();
        m.setModifyTimestamp(timestamp);
        m.setModifyDatetime(LocalDateTime.now());
        return m;
    }
    
    private static <M extends Model> Collection<M> init(Collection<M> ms) {
        return ms.stream().map(JGPRepositoryImpl::init).collect(Collectors.toList());
    }
    
    private static <M extends Model> Collection<M> refresh(Collection<M> ms) {
        return ms.stream().map(JGPRepositoryImpl::refresh).collect(Collectors.toList());
    }
    
    private static long nanoTime() {
        Long nanoTime = System.nanoTime(); // 纳秒
        return System.currentTimeMillis() * 1000000 + (nanoTime - nanoTime / 1000000 * 1000000);
    }
    
    @Transactional
    @Override
    public M createOrUpdate(M m) {
        if (Objects.isNull(m.getId())) {
            return this.create(m);
        } else {
            this.update(m);
            return readPersist((ID) m.getId());
        }
    }
    
    @Transactional
    @Override
    public M create(M m) {
        init(m);
        if (TreeModelStructure.class.isAssignableFrom(javaType)) {
            TreeModel treeModel = (TreeModel) m;
            if (StringUtils.isBlank(treeModel.getSupers())) {
                Long parentId = treeModel.getParentId();
                if (parentId == -1) {
                    treeModel.setSupers("-1");
                } else {
                    TreeModel parent = (TreeModel) read((ID) parentId);
                    treeModel.setSupers(parent.getSupers() + "," + parentId);
                    treeModel.setUuid(JGPUtil.uuid());
                }
            }
        }
        if (UUIDModel.class.isAssignableFrom(javaType) && Objects.isNull(m.getId())) {
            m.setId(JGPUtil.uuid());
        }
        return super.save(m);
    }
    
    @Transactional
    @Override
    public List<M> createInBatch(List<M> ms) {
        List<M> msList = new ArrayList();
        init(ms).forEach(m -> {
            this.entityManager.persist(m);
            msList.add(m);
        });
        return msList;
    }
    
    
    @Transactional
    @Override
    public M update(M m, String... columns) {
        Assert.notNull(m, "实体对象不能为空!");
        Assert.notNull(m.getId(), "更新操作ID不能为空!");
        M persist = findById((ID) m.getId()).get();
        packageUpdateMIncludes(persist, m, columns);
        return super.save(persist);
    }
    
    @Transactional
    @Override
    public M updateExcludes(M m, String... columns) {
        
        Assert.notNull(m, "实体对象不能为空!");
        Assert.notNull(m.getId(), "更新操作ID不能为空!");
        
        if (Objects.isNull(columns) || columns.length == 0) {
            return this.update(m);
        } else {
            M persist = findById((ID) m.getId()).get();
            packageUpdateMExcludes(persist, m, columns);
            return super.save(persist);
        }
    }
    
    @Transactional
    @Override
    public long updateInBatch(List<M> ms, String... columns) {
        Assert.notNull(ms, "实体对象不能为空!");
        List<M> persists = updateInBatch(ms, true, columns);
        return super.saveAll(persists).size();
    }
    
    
    @Transactional
    @Override
    public long updateInBatchExcludes(List<M> ms, String... columns) {
        Assert.notNull(ms, "实体对象不能为空!");
        List persist = updateInBatch(ms, false, columns);
        return super.saveAll(persist).size();
    }
    
    private List<M> updateInBatch(List<M> ms, boolean includeFlag, String... columns) {
        List<ID> ids = ms.stream().map(m -> (ID) m.getId()).collect(Collectors.toList());
        List<M> persists = super.findAllById(ids);
        Map<ID, M> updates = new HashMap<>();
        ms.forEach(m -> updates.put((ID) m.getId(), m));
        
        List needToPersists = persists.stream().map(persist -> {
            M update = updates.get(persist.getId());
            if (includeFlag) {
                packageUpdateMIncludes(persist, update, columns);
            } else {
                packageUpdateMExcludes(persist, update, columns);
            }
            return persist;
        }).collect(Collectors.toList());
        
        return needToPersists;
    }
    
    private M packageUpdateMIncludes(M persist, M update, String... columns) {
        if (Objects.isNull(columns) || columns.length == 0) {
            List<String> columnsList = new ArrayList<>();
            for (Field field : ReflectUtil.getPersistFields(this.javaType)) {
                if (!ArrayUtils.contains(PLATFORM_COLUMNS, field.getName())) {
                    columnsList.add(field.getName());
                }
            }
            columns = columnsList.toArray(new String[columnsList.size()]);
        }
        return packageUpdateM(persist, update, columns);
    }
    
    private M packageUpdateMExcludes(M persist, M update, String... columns) {
        List<String> columnsList = new ArrayList<>();
        for (Field field : ReflectUtil.getPersistFields(this.javaType)) {
            if (!ArrayUtils.contains(PLATFORM_COLUMNS, field.getName()) && !ArrayUtils.contains(columns, field.getName())) {
                columnsList.add(field.getName());
            }
            columns = columnsList.toArray(new String[columnsList.size()]);
        }
        return packageUpdateM(persist, update, columns);
    }
    
    
    private M packageUpdateM(M persist, M update, String... columns) {
        for (String column : columns) {
            Object value = ReflectUtil.getFieldValueByName(update, column);
            ReflectUtil.setFieldValue(persist, column, value);
        }
        refresh(persist);
        return persist;
    }
    
    @Transactional
    @Override
    public void delete(M m) {
        Assert.notNull(m.getId(), "对象ID不能为空!");
        m.setDelFlag(true);
        update(m, "delFlag");
    }
    
    @Transactional
    @Override
    public void delete(ID id) {
        M m = readPersist(id);
        m.setDelFlag(true);
        update(m, "delFlag");
    }
    
    @Transactional
    @Override
    public void deleteInBatch(List<M> ms) {
        for (M m : ms) {
            m.setDelFlag(true);
        }
        updateInBatch(ms, "delFlag");
    }
    
    @Transactional
    @Override
    public void deletePersist(M m) {
        Assert.notNull(m, "删除实体不能为 NULL!");
        this.entityManager.remove(this.entityManager.contains(m) ? m : this.entityManager.merge(m));
    }
    
    @Transactional
    @Override
    public void deletePersist(ID id) {
        Assert.notNull(id, "ID 不能为空");
        M entity = readPersist(id);
        Assert.notNull(entity, "删除实体不能为 NULL!");
        this.entityManager.remove(this.entityManager.contains(entity) ? entity : this.entityManager.merge(entity));
    }
    
    @Transactional
    @Override
    public void deleteInBatchPersist(List<M> ms) {
        super.deleteInBatch(ms);
    }
    
    @Transactional
    @Override
    public void deleteInBatchPersistByIds(List<ID> ids) {
        super.deleteInBatch(readAllPersist(ids));
    }
    
    @Transactional
    @Override
    public void deleteAll() {
        CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
        CriteriaUpdate<M> criteriaUpdate = cb.createCriteriaUpdate(javaType);
        criteriaUpdate.set("delFlag", true);
        Query query = entityManager.createQuery(criteriaUpdate);
        query.executeUpdate();
    }
    
    @Transactional
    @Override
    public void deleteAllPersist() {
        super.deleteAllInBatch();
    }
    
    @Transactional
    @Override
    public long delete(QueryFilterList filters) {
        CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
        CriteriaUpdate<M> criteriaUpdate = cb.createCriteriaUpdate(javaType);
        Root root = criteriaUpdate.from(javaType);
        criteriaUpdate.set("delFlag", true);
        criteriaUpdate.where(CriteriaExpression.toPredicate(filters, root, cb));
        Query query = entityManager.createQuery(criteriaUpdate);
        return query.executeUpdate();
    }
    
    @Transactional
    @Override
    public long deletePersist(QueryFilterList filters) {
        CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
        CriteriaDelete<M> criteriaDelete = cb.createCriteriaDelete(javaType);
        Root root = criteriaDelete.from(javaType);
        criteriaDelete.where(CriteriaExpression.toPredicate(filters, root, cb));
        Query query = entityManager.createQuery(criteriaDelete);
        return query.executeUpdate();
    }
    
    @Override
    public M readOne(QueryFilterList filters) {
        List<M> list = read(filters);
        if (Objects.nonNull(list) && list.size() > 0) {
            return list.get(0);
        } else {
            return null;
        }
    }
    
    @Override
    public M readOnePersist(QueryFilterList filters) {
        List<M> list = readPersist(filters);
        if (Objects.nonNull(list) && list.size() > 0) {
            return list.get(0);
        } else {
            return null;
        }
    }
    
    @Override
    public List<M> read(QueryFilterList filters, final String... columns) {
        CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
        CriteriaQuery<Tuple> criteriaQuery = cb.createTupleQuery();
        Root root = criteriaQuery.from(javaType);
        if (Objects.nonNull(columns)) {
            List<Selection<?>> selections = new ArrayList<>();
            for (String column : columns) {
                selections.add(root.get(column).alias(column));
            }
            criteriaQuery.multiselect(selections);
        }
        criteriaQuery.where(CriteriaExpression.toPredicate(filters, root, cb));
        TypedQuery query = entityManager.createQuery(criteriaQuery);
        List<Tuple> tuples = query.getResultList();
        return tuples.parallelStream().map(tuple -> {
            Map<String, Object> map = new HashMap<>();
            for (String column : columns) {
                map.put(column, tuple.get(column));
            }
            return JGPUtil.toBean(map, javaType);
        }).collect(Collectors.toList());
    }
    
    @Override
    public M read(ID id) {
        M m = readPersist(id);
        if (Objects.isNull(m)) return null;
        if (m.getDelFlag()) {
            return null;
        }
        return m;
    }
    
    @Override
    public List<M> read(Collection<ID> ids) {
        List<M> ms = readPersist(ids);
        if (Objects.isNull(ms)) return null;
        return ms.stream().filter(m -> !m.getDelFlag()).collect(Collectors.toList());
    }
    
    @Override
    public List<M> readAll() {
        return readAllPersist().stream().filter(m -> !m.getDelFlag()).collect(Collectors.toList());
    }
    
    @Override
    public List<M> readAllPower(DataPowerConfig powerConfig) {
        QueryFilterList filters = new QueryFilterList();
        filters.addFilter("delFlag", "eq", false);
        return readPersistPower(filters, powerConfig);
    }
    
    @Override
    public List<M> readAll(List<ID> ids) {
        return readAllPersist(ids).stream().filter(m -> !m.getDelFlag()).collect(Collectors.toList());
    }
    
    @Override
    public M readPersist(ID id) {
        try {
            return super.findById(id).get();
        } catch (NoSuchElementException e) {
            return null;
        }
    }
    
    @Override
    public List<M> readPersist(Collection<ID> ids) {
        try {
            return super.findAllById(ids);
        } catch (NoSuchElementException e) {
            return null;
        }
    }
    
    @Override
    public List<M> readAllPersist() {
        return super.findAll();
    }
    
    @Override
    public List<M> readAllPersistPower(DataPowerConfig powerConfig) {
        return readPersistPower(null, powerConfig);
    }
    
    @Override
    public List<M> readAllPersist(Collection<ID> ids) {
        return super.findAllById(ids);
    }
    
    @Override
    public List<M> read(QueryFilterList filters) {
        return read(filters, null, null, null);
    }
    
    @Override
    public List<M> read(QueryFilterList filters, SpecPredicate<M> spec) {
        return read(filters, null, spec, null);
    }
    
    @Override
    public List<M> readPower(QueryFilterList filters, DataPowerConfig powerConfig) {
        return readPower(filters,null, null,null,powerConfig);
    }
    
    @Override
    public List<M> readPower(QueryFilterList filters, SpecPredicate<M> spec, DataPowerConfig powerConfig) {
        return readPower(filters,null, spec, powerConfig);
    }
    
    @Override
    public List<M> read(QueryFilterList queries, Pager pager) {
        return read(queries, null, null, pager);
    }
    
    @Override
    public List<M> read(QueryFilterList filters, SpecPredicate<M> spec, Pager pager) {
        return read(filters, null, spec, pager);
    }
    
    @Override
    public List<M> readPower(QueryFilterList filters, Pager pager, DataPowerConfig powerConfig) {
        return readPower(filters, null, null, pager, powerConfig);
    }
    
    @Override
    public List<M> readPower(QueryFilterList filters, SpecPredicate<M> spec, Pager pager, DataPowerConfig powerConfig) {
        return readPower(filters, null, spec, pager, powerConfig);
    }
    
    @Override
    public List<M> read(QueryFilterList filters, OrderList orders) {
        return read(filters, orders, null, null);
    }
    
    @Override
    public List<M> read(QueryFilterList filters, OrderList orders, SpecPredicate<M> spec) {
        return read(filters, orders, spec, null);
    }
    
    @Override
    public List<M> readPower(QueryFilterList filters, OrderList orders, DataPowerConfig powerConfig) {
        return readPower(filters, orders,null,null, powerConfig);
    }
    
    @Override
    public List<M> readPower(QueryFilterList filters, OrderList orders, SpecPredicate<M> spec, DataPowerConfig powerConfig) {
        return readPower(filters, orders, spec, null, powerConfig);
    }
    
    @Override
    public List<M> read(QueryFilterList filters, OrderList orders, Pager pager) {
        return read(filters, orders, null, pager);
    }
    
    @Override
    public List<M> read(QueryFilterList filters, OrderList orders, SpecPredicate<M> spec, Pager pager) {
        DataPowerConfig config = new DataPowerConfig();
        config.setEnabled(false);
        return readPower(filters, orders, spec, pager, config);
    }
    
    @Override
    public List<M> readPower(QueryFilterList filters, OrderList orders, Pager pager, DataPowerConfig powerConfig) {
        return readPower(filters, orders, null, pager, powerConfig);
    }
    
    @Override
    public List<M> readPower(QueryFilterList filters, OrderList orders, SpecPredicate<M> spec, Pager pager, DataPowerConfig powerConfig) {
        if (Objects.isNull(filters)) filters = new QueryFilterList();
        filters.addFilter("delFlag", "eq", false);
        return readPersistPower(filters, orders, spec, pager, powerConfig);
    }
    
    @Override
    public List<Map<String, Object>> readNativeSqlToMap(String countSql, String sql, Pager pager) {
        
        List<Map<String, Object>> list = readNativeSqlToMapWithOutTransformName(countSql, sql, pager);
        return list.stream().map(map -> {
            Map<String, Object> m = new HashMap();
            map.forEach((key, value) -> {
                String fieldName = JGPUtil.underline2Camel(key, true);
                m.put(fieldName, value);
            });
            return m;
        }).collect(Collectors.toList());
    }
    
    @Override
    public List<Map<String, Object>> readNativeSqlToMapWithOutTransformName(String countSql, String sql, Pager pager) {
        
        Query query = this.entityManager.createNativeQuery(bindClient(sql));
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        setPageInfo(query, bindClient(countSql), pager);
        
        return query.getResultList();
    }
    
    @Override
    public List<M> readNativeSql(String countSql, String sql, Pager pager) {
        Query query = this.entityManager.createNativeQuery(bindClient(sql));
        query.unwrap(NativeQueryImpl.class).setResultTransformer(new ToBeanResultTransformer(this.javaType));
        setPageInfo(query, bindClient(countSql), pager);
        List<M> list = query.getResultList();
        return list;
    }
    
    private Pager setPageInfo(Query query, String countSql, Pager pager) {
        if (Objects.isNull(pager)) return null;
        Query countQuery = this.entityManager.createNativeQuery(countSql);
        List<Number> countResultList = countQuery.getResultList();
        if (Objects.isNull(countResultList) || countResultList.size() == 0 || countResultList.get(0).longValue() == 0) {
            pager.setTotalCount(0);
            pager.setTotalPageCount(1);
        } else {
            long totalCount = countResultList.get(0).longValue();
            pager.setTotalCount(totalCount);
            if (totalCount % pager.getPageSize() > 0) {
                pager.setTotalPageCount((totalCount / pager.getPageSize()) + 1);
            } else {
                pager.setTotalPageCount(totalCount / pager.getPageSize());
            }
        }
        query.setMaxResults(pager.getPageSize());
        query.setFirstResult((pager.getCurrent() - 1) * pager.getPageSize());
        return pager;
    }
    
    @Override
    public List<Map<String, Object>> readNativeSqlToMap(String sql) {
        Query query = this.entityManager.createNativeQuery(bindClient(sql));
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        return query.getResultList();
    }
    
    @Override
    public List<M> readNativeSql(String sql) {
        Query query = this.entityManager.createNativeQuery(bindClient(sql));
        query.unwrap(NativeQueryImpl.class).setResultTransformer(new ToBeanResultTransformer(this.javaType));
        return query.getResultList();
    }
    
    @Override
    public Map<String, Object> readOneNativeSqlToMap(String sql) {
        Query query = this.entityManager.createNativeQuery(bindClient(sql));
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        Object model = query.getSingleResult();
        if (Objects.nonNull(model)) return JGPUtil.toMap(model);
        return null;
    }
    
    @Override
    public M readOneNativeSql(String sql) {
        Query query = this.entityManager.createNativeQuery(bindClient(sql));
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(this.javaType));
        return (M) query.getSingleResult();
    }
    
    @Override
    public int executeNativeSql(String sql, Object... params) {
        Query query = this.entityManager.createNativeQuery(bindClient(sql));
        if (Objects.nonNull(params)) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i + 1, params[i]);
            }
        }
        return query.executeUpdate();
    }
    
    private String bindClient(String sql){
        /*String clientId = filterBuilder.clientId(this.javaType);
        StringBuffer result = new StringBuffer();
        if(StringUtils.isNotBlank(clientId)){
            String[] sqls = sql.split("where");
            for(int i=0;i<sqls.length;i++){
                if(i == sqls.length-1){
                    result.append(sqls[i]);
                }else{
                    result.append(sqls[i]+" where client_id='"+clientId+"' and ");
                }
            }
            return result.toString();
        }*/
        return sql;
    }
    
    @Override
    public List<M> readPersist(QueryFilterList queries) {
        return readPersist(queries, new OrderList());
    }
    
    @Override
    public List<M> readPersist(QueryFilterList filters, SpecPredicate<M> spec) {
        return readPersist(filters, null, spec);
    }
    
    @Override
    public List<M> readPersistPower(QueryFilterList filters, DataPowerConfig powerConfig) {
        return readPersistPower(filters, null, null, null, powerConfig);
    }
    
    @Override
    public List<M> readPersistPower(QueryFilterList filters, SpecPredicate<M> spec, DataPowerConfig powerConfig) {
        return readPersistPower(filters, null, spec, null, powerConfig);
    }
    
    @Override
    public List<M> readPersist(QueryFilterList filters, Pager pager) {
        return readPersist(filters, null, null, pager);
    }
    
    @Override
    public List<M> readPersist(QueryFilterList filters, SpecPredicate<M> spec, Pager pager) {
        return readPersist(filters, null, spec, pager);
    }
    
    @Override
    public List<M> readPersistPower(QueryFilterList filters, Pager pager, DataPowerConfig powerConfig) {
        return readPersistPower(filters, null, null, pager, powerConfig);
    }
    
    @Override
    public List<M> readPersistPower(QueryFilterList filters, SpecPredicate<M> spec, Pager pager, DataPowerConfig powerConfig) {
        return readPersistPower(filters, null, spec, pager, powerConfig);
    }
    
    @Override
    public List<M> readPersist(QueryFilterList filters, OrderList orders) {
        DataPowerConfig config = new DataPowerConfig();
        config.setEnabled(false);
        return readPersistPower(filters, orders, config);
    }
    
    @Override
    public List<M> readPersist(QueryFilterList filters, OrderList orders, SpecPredicate<M> spec) {
        return readPersist(filters, orders, spec, null);
    }
    
    @Override
    public List<M> readPersistPower(QueryFilterList filters, OrderList orders, DataPowerConfig powerConfig) {
        return readPersistPower(filters, orders, null, null, powerConfig);
    }
    
    @Override
    public List<M> readPersistPower(QueryFilterList filters, OrderList orders, SpecPredicate<M> spec, DataPowerConfig powerConfig) {
        if (Objects.isNull(orders) || orders.size() == 0) {
            return super.findAll((root, query, builder) -> filters(filters, spec, root, query, builder, powerConfig));
        } else {
            return super
                    .findAll((root, query, builder) -> filters(filters, spec, root, query, builder, powerConfig), CriteriaExpression.toSort(orders));
        }
    }
    
    @Override
    public List<M> readPersist(QueryFilterList filters, OrderList orders, Pager pager) {
        DataPowerConfig config = new DataPowerConfig();
        config.setEnabled(false);
        return readPersistPower(filters, orders, pager, config);
    }
    
    @Override
    public List<M> readPersist(QueryFilterList filters, OrderList orders, SpecPredicate<M> spec, Pager pager) {
        return readPersistPower(filters, orders, spec, pager, null);
    }
    
    @Override
    public List<M> readPersistPower(QueryFilterList filters, OrderList orders, Pager pager, DataPowerConfig powerConfig) {
        return readPersistPower(filters, orders, null, pager, powerConfig);
    }
    
    @Override
    public List<M> readPersistPower(QueryFilterList filters, OrderList orders, SpecPredicate<M> spec, Pager pager, DataPowerConfig powerConfig) {
        Pageable pageable = null;
        if (Objects.nonNull(pager)) {
            pageable = pager.pageable(orders);
        }
        if (Objects.nonNull(pageable)) {
            Page<M> page = super.findAll((root, query, builder) -> filters(filters, spec, root, query, builder, powerConfig), pageable);
            
            pager.setTotalCount(page.getTotalElements());
            pager.setTotalPageCount(page.getTotalPages());
            return page.getContent();
        } else {
            return readPersistPower(filters, orders, spec, powerConfig);
        }
    }
    
    private Predicate filters(QueryFilterList filters, SpecPredicate<M> spec, Root<M> root, CriteriaQuery<?> query, CriteriaBuilder builder,
                              DataPowerConfig powerConfig) {
        Predicate[] filtersPredicates = CriteriaExpression.toPredicate(filters, root, builder);
        Predicate dataFilter = dataFilter(root, query, builder, powerConfig);
        Predicate specPredicate = null;
        if (Objects.nonNull(spec)) {
            specPredicate = spec.build(root, query, builder);
        }
        List<Predicate> queries = new ArrayList<>();
        queries.addAll(new ArrayList<>(Arrays.asList(filtersPredicates)));
        queries.add(dataFilter);
        if(Objects.nonNull(specPredicate)){
            queries.add(specPredicate);
        }
        List<Predicate> predicates = queries.stream().filter(Objects::nonNull)
                                           .collect(Collectors.toList());
        return builder.and(predicates.toArray(new Predicate[predicates.size()]));
    }
    
    private Predicate dataFilter(Root<M> root, CriteriaQuery<?> query, CriteriaBuilder builder, DataPowerConfig powerConfig) {
        Predicate clientFilter = filterBuilder.clientFilter(root,builder);
        if (Objects.isNull(powerConfig) || powerConfig.isEnabled()) {
            List<Predicate> predicates = filterBuilder.doFilters(root, query, builder, powerConfig);
            predicates.add(clientFilter);
            return predicates.size()>0?builder.and(predicates.toArray(new Predicate[predicates.size()])):null;
        }
        return clientFilter;
    }
    
    @Override
    public long countAll() {
        return super.count((root, criteriaQuery, criteriaBuilder) -> criteriaBuilder.equal(root.get("delFlag"), false));
    }
    
    @Override
    public long countAllPersist() {
        return super.count();
    }
    
    @Override
    public long count(QueryFilterList filters) {
        return count(filters, null);
    }
    
    @Override
    public long count(QueryFilterList filters, SpecPredicate<M> spec) {
        DataPowerConfig config = new DataPowerConfig();
        config.setEnabled(false);
        return countPower(filters, spec, config);
    }
    
    @Override
    public long countPower(QueryFilterList filters, DataPowerConfig powerConfig) {
        return countPower(filters, null, powerConfig);
    }
    
    @Override
    public long countPower(QueryFilterList filters, SpecPredicate<M> spec, DataPowerConfig powerConfig) {
        if (Objects.isNull(filters)) filters = new QueryFilterList();
        filters.addFilter("delFlag", "eq", false);
        return countPersistPower(filters, spec, powerConfig);
    }
    
    @Override
    public long countPersist(QueryFilterList filters) {
        return countPersist(filters, null);
    }
    
    @Override
    public long countPersist(QueryFilterList filters, SpecPredicate<M> spec) {
        DataPowerConfig config = new DataPowerConfig();
        config.setEnabled(false);
        return countPersistPower(filters, config);
    }
    
    @Override
    public long countPersistPower(QueryFilterList filters, DataPowerConfig powerConfig) {
        return countPersistPower(filters, null, powerConfig);
    }
    
    @Override
    public long countPersistPower(QueryFilterList filters, SpecPredicate<M> spec, DataPowerConfig powerConfig) {
        return super.count((root, query, builder) -> filters(filters, spec, root, query, builder, powerConfig));
    }
    
    @Override
    public List<TreeBean> queryTree(QueryFilterList filters, OrderList orders) {
        List<M> nodes = read(filters, orders);
        return TreeUtil.createTree(nodes);
    }
    
    @Override
    public List<TreeBean> queryTreeFromBottomToTop(ID nodeId) {
        M model = read(nodeId);
        TreeModelStructure node = (TreeModelStructure) model;
        List<ID> ids = new ArrayList<>();
        for (String id : StringUtils.split(node.getSupers(), ",")) {
            ids.add((ID) ReflectUtil.cast(id, idType));
        }
        List<M> nodes = read(ids);
        return TreeUtil.createTree(nodes);
    }
    
    @Override
    public List<TreeBean> queryTreeFromBottomToTop(List<ID> nodeIds) {
        List<M> models = read(nodeIds);
        Set<ID> ids = new HashSet<>();
        models.forEach(m -> {
            TreeModelStructure node = (TreeModelStructure) m;
            for (String id : StringUtils.split(node.getSupers(), ",")) {
                ids.add((ID) ReflectUtil.cast(id, idType));
            }
        });
        List<M> nodes = read(ids);
        nodes.addAll(models);
        return TreeUtil.createTree(nodes);
    }
    
    @Transactional
    @Override
    public List<ID> deleteTreeItemAndAllChildren(QueryFilterList filters) {
        List<M> ms = new ArrayList<>();
        List<M> nodes = read(filters);
        ms.addAll(nodes);
        nodes.forEach(m -> {
            TreeModel treeModel = (TreeModel) m;
            String supers = treeModel.getSupers() + "," + treeModel.getId();
            QueryFilterList nestedFilters = new QueryFilterList();
            nestedFilters.addFilter("supers", Operator.likeL, supers);
            List<M> nested = read(nestedFilters);
            if (Objects.nonNull(nested) && nested.size() > 0) {
                ms.addAll(nested);
                delete(nestedFilters);
            }
        });
        delete(filters);
        return ms.stream().map(m -> (ID) m.getId()).collect(Collectors.toList());
    }
}
